iT邦幫忙

2021 iThome 鐵人賽

DAY 23
0
Modern Web

新新新手閱讀 Angular 文件30天系列 第 23

新新新手閱讀 Angular 文件 - Component - Day23

  • 分享至 

  • xImage
  •  

本文內容

本文是閱讀有關 Angular 的元件生命週期的 OnChanges 的筆記內容。

ngOnChanges

呼叫時機: 當元件中的 @Input 類型的屬性,發生變化的時候,這個 lifecycle hook 就會被呼叫。
例如:
[TypeScript]

import { OnChanges, SimpleChanges, Input} from '@angular/core';

export class AppComponent implements OnChanges {
  @Input() book;

  ngOnChanges(changes: SimpleChanges) {
    console.log(this.book);
  }
}

上面的範例中,可以看到在這個元件裡面定義了一個 @Input 的屬性 book,每當 book 的內容有變化的時候,就會觸發到 ngOnChanges 的 hook 喔。
ngOnChanges 會自帶一個參數,他是一個物件,裡面會包含的內容為 @Input 屬性的現在的值和上一次的值。

傳址的問題

這邊我們先來寫個 @InputngOnChanges 的範例
[子元件 - TypeScript]

@Component({
 selector: 'bcomp',
 template: `
   <div *ngFor="let user of users">
     {{ user }}
   </div>
 `
})
export class BComponent implements OnChanges {
 @Input() users
 ngOnChanges() {
   console.log("changed")
 }
}

[父元件 - TypeScript]

@Component({
 template: `
   <bcomp [users]="users"></bcomp>
 `
})
export class App {
 users = ["Jasper", "Tom", "Mary"]
}

可以看到在子元件定義了一個 @Input property 叫做 users,並把這個屬性綁到父元件的裡面,去接收來自父元件的 users 內容。
上面的內容,會呈現如下的畫面
https://ithelp.ithome.com.tw/upload/images/20210923/20140093etcfBcT7U4.png

就很單純的把資料渲染到畫面上。

那如果今天當點擊某個按鈕,會逐一刪除 users 裡面的內容,那承如上面所講過的當 @Input 屬性的內容改變的時候,照道理講子元件的 ngOnChanges 這個 hook 會被觸發。
那來改寫一下上面的範例吧
[子元件 - TypeScript]

export class BComponent implements OnInit, OnChanges {
  @Input() users;
  
  ngOnChanges(obj: SimpleChanges) {
    console.log(obj);
  }
}

[父元件 - TypeScript]

@Component({
  template: `
    <app-b [users]="users"></app-b>
    <button (click)="delete()">delete</button>
  `,
})
export class AppComponent {
  users = ['Jasper', 'Tom', 'Mary'];

  delete() {
    this.users.splice(0, 1);
  }
}

可以看到上面的範例,在父元件有個 delete 的函式,每當點擊按鈕就會觸發它,並刪除 users 的陣列的內容。
操作畫面如下

可以看到畫面上的陣列內容,確實地逐一被刪除,但是, console.log 中並沒有觸發子元件的 ngOnChanges 裡面的內容,將改變的內容印出來。
原因是什麼呢?
因為,users 陣列透過 splice 來刪除它的陣列內容,但並沒有改變它陣列的位址,所以,對 @Input 屬性 users 來說,它都是指向同一個位址的陣列,儘管它內容有被刪減,對 @Input 屬性 users 來講還是同一個人根本沒變化,所以,才沒有觸發它的 ngOnChanges 內容囉。

那要怎麼改呢?
很簡單,我們就用那種會回傳新陣列的刪除方法,也就是 slice
那我們來改寫一下,父元件的內容
[父元件 - TypeScript]

@Component({
  template: `
    <app-b [users]="users"></app-b>
    <button (click)="delete()">delete</button>
  `,
})
export class AppComponent {
  users = ['Jasper', 'Tom', 'Mary'];

  delete() {
    this.users = this.users.slice(1);
  }
}

你可以看到,我們把經過 slice 刪除後產生的陣列回傳給原本的 users,讓它去指向另一個位址的新陣列。
操作結果如下

可以看到子元件的 ngOnChanges 函式就會被觸發囉,而且我們可以透過印出 ngOnChanges 參數內容,去看每一次子元件的 users 陣列內容是真的也被刪除掉了。

Summary

來做個總結

  1. ngOnChanges 會在元件的 @Input 屬性有改變的時候,被觸發。
  2. 要注意在 @Input 內容的傳址問題,會導致子元件的 ngOnChanges 不會被觸發,若要改變這種現象的話,就必須使用 non-mutating method (也就是會回傳新的物件的方式) 來達成。

Reference

  1. Introduce ngOnChanges
  2. Angular OnChanges officail doc
  3. Non-mutating methods intro

上一篇
新新新手閱讀 Angular 文件 - Component - Day22
下一篇
新新新手閱讀 Angular 文件 - Component - Day24
系列文
新新新手閱讀 Angular 文件30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言